package com.bestcem.xm.member.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.sql.ColumnDesc;
import com.bestcem.xm.common.core.sql.SQLConditionCompiler;
import com.bestcem.xm.common.core.uitls.JsonUtil;
import com.bestcem.xm.member.dao.MemberDao;
import com.bestcem.xm.member.entity.mysql.MemberEntity;
import com.bestcem.xm.member.entity.pojo.MemberDO;
import com.bestcem.xm.member.enums.NewMemberStatusEnum;
import com.bestcem.xm.member.grpc.service.param.member.ListMemberInfoByFilterParamsParam;
import com.bestcem.xm.member.service.FuzzyMemberService;
import com.bestcem.xm.member.service.MemberUserGroupService;
import com.bestcem.xm.member.service.dto.MemberDTO;
import com.bestcem.xm.member.service.param.FuzzyMemberDynamicSql;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.member.constant.Constants.PUSH_TYPE_MAP;

/**
 * 示例服务
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/9/25
 */
@Slf4j
@Service
public class FuzzyMemberServiceImpl implements FuzzyMemberService {
    @Resource
    private MemberDao memberDao;

    @Resource
    private MemberUserGroupService memberUserGroupService;

    /**
     * 根据过滤条件获取联系人信息
     * <p>
     * 注意 因为 python member_display_condition 是固定的，目前没有接收这个参数
     *
     * @param param
     * @return
     */
    @Override
    public ServiceResult<List<MemberDTO>> listMemberInfoByFilterParams(ListMemberInfoByFilterParamsParam param) {

        JSONObject paramJson = null;
        try {
            // 解析 param
            paramJson = JSONObject.parseObject(param.getParam());
        } catch (Exception e) {
            log.error("json 解析失败", e);
            return ServiceResult.fail("过滤条件解析失败");
        }

        // 弹出免打扰联系人ids, 返回时去除免打扰联系人
        List<String> notDistrubMemberList = getNotDistrubMemberList(paramJson);
        // 先判断是否是条件筛选
        String memberFilterType = paramJson.getString("member_filter_type");
        // 联系人类型
        Integer mtype = paramJson.getInteger("mtype");
        // 投放类型
        String pushType = paramJson.getString("push_type");
        // appId
        String appId = paramJson.getString("app_id");

        if ("wechat".equals(pushType)) {
            if (StrUtil.isBlank(appId)) {
                log.error("微信投放, 缺少appId");
                return ServiceResult.fail("微信投放, 缺少appId");
            }
        }

        List<MemberDO> members = null;
        if ("in_condition".equals(memberFilterType)) {
            // 如果条件筛选
            ServiceResult<FuzzyMemberDynamicSql> documentServiceResult =
                    parseDeliverMembersCondition(mtype, param.getOrgId(), paramJson, pushType, appId);
            if (!documentServiceResult.isSuccess()) {
                // 条件解析失败
                return ServiceResult.fail(documentServiceResult.getMsg());
            }
            // 查询联系人
            members = memberDao.filterMembers(documentServiceResult.getData());
        } else {
            // 不是条件筛选 python 那写死了 return_val 所以目前该方法只写了 return_val = model 的逻辑
            members = queryParamsByMemberPushCondition(param.getOrgId(), pushType, paramJson, "model", appId);
        }

        // 过滤组织层级
        if(!Boolean.TRUE.equals(param.getSuperAdmin()) && !Boolean.TRUE.equals(param.getSeniorAdmin())){
            Set<String> storeIds = members.stream().map(MemberDO::getStoreId).filter(StringUtils::hasText).collect(Collectors.toSet());
            log.error("待过滤联系人组织:{}", storeIds);

            if(!CollectionUtils.isEmpty(storeIds)){
                ServiceResult<Collection<String>> result = memberUserGroupService.checkUserStorePermissions(param.getUserId(), storeIds);
                if (!result.isSuccess()) {
                    result.setData(new HashSet<>());
                }
                log.error("已过滤联系人组织:{}", result.getData());
                members = members.stream().filter(m -> !StringUtils.hasText(m.getStoreId())
                        || result.getData().contains(m.getStoreId())).collect(Collectors.toList());
            }
        }


        if (CollUtil.isEmpty(members)) {
            return ServiceResult.success();
        }
        //members需要转化为id列表
        List<String> ids = members.stream().map(member -> member.getId()).collect(Collectors.toList());
        // 提前处理免打扰
        ids.removeAll(notDistrubMemberList);
        // 处理随机投放
        JSONObject randomSampling = paramJson.getJSONObject("random_sampling");
        if (ObjectUtil.isNotEmpty(randomSampling)) {
            // 获取查询的联系人数量
            int size = ids.size();
            // 按数量或者比例 应该随机得到的联系人数量
            ServiceResult<Integer> selectNumsResult = dealRandomSampling(randomSampling, size);
            if (!selectNumsResult.isSuccess()) {
                return ServiceResult.fail(selectNumsResult.getMsg());
            }

            Integer selectNum = selectNumsResult.getData();
            ids = RandomUtil.randomEleList(ids, selectNum);
            if (log.isInfoEnabled()) {
                log.info("随机的联系人数量 {} id 是{}", selectNum, ids);
            }
        }
        List<MemberDTO> memberDTOS = ids.stream().map(id -> {
            MemberDTO dto = new MemberDTO();
            dto.setId(id);
            dto.setName("deliver");
            return dto;
        }).collect(Collectors.toList());
        return ServiceResult.success(memberDTOS);
    }

    /**
     * 获取免打扰联系人
     *
     * @param paramJson
     * @return
     */
    private List<String> getNotDistrubMemberList(JSONObject paramJson) {
        if (paramJson.containsKey("not_distrub_member")) {
            JSONArray notDistrubMembers = paramJson.getJSONArray("not_distrub_member");
            if (!notDistrubMembers.isEmpty()) {
                return notDistrubMembers.stream().map(String::valueOf).collect(Collectors.toList());
            }
        }
        return new ArrayList<>();
    }

    /**
     * 投放时筛选联系人 处理，添加了联系人显示，联系人投放方式的过滤
     *  @param mtype
     * @param orgId
     * @param paramJson
     * @param pushType
     * @param appId
     */
    private ServiceResult<FuzzyMemberDynamicSql> parseDeliverMembersCondition(Integer mtype, String orgId,
                                                                              JSONObject paramJson, String pushType, String appId) {
        FuzzyMemberDynamicSql sql = new FuzzyMemberDynamicSql();
        sql.setOrgId(orgId);
        sql.setMemberType(mtype);
        sql.setStatus(NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
        sql.setAppId(appId);
        if (StrUtil.isNotEmpty(pushType)) {
            // 若存在 投放类型
            sql.setPushType(PUSH_TYPE_MAP.get(pushType));
        }
        // 解析condition
        ServiceResult<Wrapper<MemberEntity>> result = null;
        String condition = paramJson.getString("condition");
        if (StrUtil.isNotEmpty(condition)) {
            try {
                Map<String, ColumnDesc> columnMapping = new HashMap<>();
                columnMapping.put("member_id", ColumnDesc.newInstance("id"));
                columnMapping.put("mobile", ColumnDesc.newInstance("phone"));
                columnMapping.put("store", ColumnDesc.newInstance("store_id", ColumnDesc.JDBC_TYPE_BINARY));
                columnMapping.put("openid", ColumnDesc.newInstance("open_id"));
                columnMapping.put("appid", ColumnDesc.newInstance("app_id"));
                columnMapping.put("alipay_user_id", ColumnDesc.newInstance("ali_pay_user_id"));
                columnMapping.put("institut_id", ColumnDesc.newInstance("institute_id"));
                columnMapping.put("institut_name", ColumnDesc.newInstance("institute_name"));
                columnMapping.put("employeeID", ColumnDesc.newInstance("employee_id"));
                result = SQLConditionCompiler.newInstance(columnMapping)
                        .compile(JsonUtil.tryConvertToJSONObject(condition));
            } catch (Exception e) {
                log.error("解析失败", e);
                return ServiceResult.fail("解析失败");
            }

            if (result.isSuccess()) {
                Wrapper<MemberEntity> data = result.getData();
                sql.setWrapper(data);
            } else {
                return ServiceResult.fail(result.getMsg());
            }
        }
        return ServiceResult.success(sql);
    }

    /**
     * 按数量或者比例 应该随机得到的联系人数量
     *
     * @param randomSampling {'type':'num', 'value':100， 'true_num'}
     * @param size           联系人总数
     * @return
     */
    private ServiceResult<Integer> dealRandomSampling(JSONObject randomSampling, int size) {
        if (!randomSampling.containsKey("type") || !randomSampling.containsKey("value") || !randomSampling.containsKey("true_num")) {
            return ServiceResult.fail("random_sampling must contain type and value and true_num");
        }
        List<String> types = new ArrayList<String>() {{
            add("num");
            add("ratio");
        }};
        String type = randomSampling.getString("type");
        if (!types.contains(type)) {
            return ServiceResult.fail("random_sampling invalid type");
        }
        Integer selectNums = null;
        try {
            selectNums = randomSampling.getInteger("true_num");
        } catch (Exception e) {
            log.error("true_num is not a num", e);
            return ServiceResult.fail("true_num is not a num");
        }
        if (selectNums < 0) {
            return ServiceResult.fail("true_num not in range");
        }
        if (selectNums > size) {
            selectNums = size;
        }
        return ServiceResult.success(selectNums);
    }

    /**
     * 根据筛选条件返回联系人查询条件
     *
     * @param orgId
     * @param pushType
     * @param param
     * @param returnVal
     * @param appId
     * @return
     */
    private List<MemberDO> queryParamsByMemberPushCondition(String orgId, String pushType,
                                                            JSONObject param,
                                                            String returnVal, String appId) {
        FuzzyMemberDynamicSql sql = new FuzzyMemberDynamicSql();
        sql.setAppId(appId);
        if (StrUtil.isNotEmpty(pushType)) {
            // 若存在 投放类型
            sql.setPushType(PUSH_TYPE_MAP.get(pushType));
        }
        // 因为 python exclude_mids 为null 所以就没有处理

        String memberFilterType = param.getString("member_filter_type");
        if ("not_in_rule".equals(memberFilterType)) {
        } else if ("in_rule".equals(memberFilterType)) {
            List<MemberDO> members = getInRuleMembers(orgId, returnVal, param, sql);
            return members;
        } else {
            if ("model".equals(returnVal)) {
                List<MemberDO> members = getMemberIdsWithoutRule(orgId, returnVal, param.getInteger("mtype"), sql);
                return members;
            }
        }
        return new ArrayList<>();
    }

    /**
     * 根据推送方式过滤符合规则的用户
     * member_display_condition  就是 documents
     *
     * @param orgId
     * @param returnVal = model
     * @param param
     * @param dynamicSql
     * @return
     */
    private List<MemberDO> getInRuleMembers(String orgId, String returnVal, JSONObject param, FuzzyMemberDynamicSql dynamicSql) {
        // 获取tags规则 但目前没有用
        JSONArray tags = param.getJSONArray("tags");
        // fields规则
        JSONArray fields = param.getJSONArray("fields");
        // members规则
        JSONArray members = param.getJSONArray("members");
        JSONArray filterRules = param.getJSONArray("filter_rule");
        // 获取联系人类型
        Integer mType = param.getInteger("mtype");
        List<String> memberIds = null;
        if (fields.isEmpty()) {
            memberIds = getMemberIdsByPushType(orgId, "$or", members, tags, fields, mType);
        }
        return getAllMemberIdsv1(orgId, memberIds, dynamicSql, mType);
    }

    /**
     * 获取 所有的联系人id
     * 因为 exclude_mids 为 null 就没有处理
     * 因为 member_ids 为 null 没有进行处理
     *
     * @param orgId
     * @param returnVal
     * @param mType
     * @param dynamicSql
     * @return
     */
    private List<MemberDO> getMemberIdsWithoutRule(String orgId,
                                                   String returnVal,
                                                   Integer mType,
                                                   FuzzyMemberDynamicSql dynamicSql) {
        // 联系人状态
        dynamicSql.setStatus(NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
        // 组织id
        dynamicSql.setOrgId(orgId);
        dynamicSql.setMemberType(mType);
        if ("model".equals(returnVal)) {
            return memberDao.filterMembers(dynamicSql);
        }
        return new ArrayList<>();
    }

    private List<MemberDO> getAllMemberIdsv1(String orgId, List<String> memberIds, FuzzyMemberDynamicSql dynamicSql, Integer mType) {

        dynamicSql.setOrgId(orgId);
        dynamicSql.setMemberType(mType);
        dynamicSql.setStatus(NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
        if (CollUtil.isNotEmpty(memberIds)) {
            dynamicSql.setMemberIds(memberIds);
        }
        List<MemberDO> queryMembers = memberDao.filterMembers(dynamicSql);
        if (log.isInfoEnabled()) {
            log.info("查询的联系人数量 {}", queryMembers.size());
        }
        return queryMembers;
    }

    /**
     * 根据推送方式规则过滤联系人
     *
     * @param orgId
     * @param rule
     * @param members
     * @param tags
     * @param fields
     * @param mType
     * @return
     */
    private List<String> getMemberIdsByPushType(String orgId, String rule, JSONArray members, JSONArray tags, JSONArray fields, Integer mType) {
        if (members.isEmpty() && tags.isEmpty() && fields.isEmpty()) {
            return new ArrayList<>();
        }
        if (!tags.isEmpty()) {
            // 暂时不实现 因为 python还是用tags处理
        }
        List<String> queryMemberIds = null;
        if (!members.isEmpty()) {
            queryMemberIds = getMemberIdsByMember(members, orgId, mType, rule);
        }
        return queryMemberIds;
    }

    /**
     * 根据 member 规则获取联系人
     * "members": [{
     * "field_name": "id",
     * "field_val": "60a21a9ff9f4f6001e008da3"
     * }, {
     * "field_name": "id",
     * "field_val": "6087b8b1aace70000c035f3e"
     * }],
     *
     * @param members
     * @param orgId
     * @param mType
     * @param rule
     * @return
     */
    private List<String> getMemberIdsByMember(JSONArray members, String orgId, Integer mType, String rule) {
        FuzzyMemberDynamicSql dynamicSql = new FuzzyMemberDynamicSql();
        List<String> memberIds = new ArrayList<>();
        for (Object member : members) {
            JSONObject memberJson = (JSONObject) JSON.toJSON(member);
            String key = memberJson.getString("field_name");
            String val = memberJson.getString("field_val");
            if ("id".equals(key) || "_id".equals(key)) {
                // 如果是id 则 特殊处理
                memberIds.add(val);
            }
        }
        dynamicSql.setOrgId(orgId);
        dynamicSql.setMemberType(mType);
        dynamicSql.setStatus(NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
        dynamicSql.setMemberIds(memberIds);
        // 查询联系人
        List<MemberDO> queryMembers = memberDao.filterMembers(dynamicSql);
        if (log.isInfoEnabled()) {
            log.info("查询的联系人数量 {}", queryMembers.size());
        }
        if (CollUtil.isNotEmpty(queryMembers)) {
            return queryMembers.stream().map(MemberDO::getId).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }
}
